package cn.itcast.account.service.impl;

import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTCCService;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
public class AccountTCCServiceImpl implements AccountTCCService {

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private AccountFreezeMapper freezeMapper;


    @Override
    @Transactional
    public void deduct(String userId, int money) {
        //1.获取事务 id
        String xid = RootContext.getXID();

        //a. 业务悬挂问题处理：判断 freeze 中是否有冻结记录，如果有，一定是 cancel 执行过，要拒绝业务
        AccountFreeze oldFreeze = freezeMapper.selectById(xid);
        if(oldFreeze != null) {
            return;
        }

        //2.扣减可用余额
        accountMapper.deduct(userId, money);
        //3.增加冻结金额，并记录当前事务的状态
        AccountFreeze freeze = new AccountFreeze();
        freeze.setUserId(userId);
        freeze.setXid(xid);
        freeze.setFreezeMoney(money);
        freeze.setState(AccountFreeze.State.TRY);
        freezeMapper.insert(freeze);
    }

    @Override
    public boolean confirm(BusinessActionContext ctx) {
        //1.添加事务 id
        String xid = RootContext.getXID();
        //2.根据 id 删除冻结记录
        int count = freezeMapper.deleteById(xid);
        return count == 1;
    }

    @Override
    public boolean cancel(BusinessActionContext ctx) {
        //1.查询冻结记录
        String xid = RootContext.getXID();
        AccountFreeze freeze = freezeMapper.selectById(xid);

        //a. 空回滚判断
        if (freeze == null) {
            //这里主要记录当前的 cancel 状态
            freeze = new AccountFreeze();
            //这里能拿到 userId 和 money 是因为在 AccountTCCService 接口中通过 BusinessActionContextParameter 注解注册了
            String userId = ctx.getActionContext("userId").toString();
            freeze.setUserId(userId);
            freeze.setXid(xid);
            freeze.setFreezeMoney(0);
            freeze.setState(AccountFreeze.State.TRY);
            freezeMapper.insert(freeze);
        }

        //b.幂等问题：第一次超时了，进行空回滚，第二次又超时了，freeze 不为空，就会进行恢复金额逻辑(这就出问题了).
        if(freeze.getState() == AccountFreeze.State.CANCEL) {
            //已经处理过依次 cancel 了，无需重复处理
            return true;
        }

        //2.恢复可用余额
        accountMapper.refund(freeze.getUserId(), freeze.getFreezeMoney());
        //3.清理冻结余额，状态修改为 cancel
        freeze.setFreezeMoney(0);
        freeze.setState(AccountFreeze.State.CANCEL);
        int count = freezeMapper.updateById(freeze);
        return count == 1;
    }

}
